"""配置变更事件处理系统。"""

import asyncio
import json
import logging
from datetime import datetime
from typing import Dict, Any, List, Optional, Callable
from enum import Enum
import redis.asyncio as redis
from fastapi import WebSocket
from motor.motor_asyncio import AsyncIOMotorDatabase

logger = logging.getLogger(__name__)


class ConfigEventType(Enum):
    """配置事件类型。"""
    
    CREATED = "config.created"
    UPDATED = "config.updated"
    DELETED = "config.deleted"
    VALIDATED = "config.validated"
    HEALTH_CHANGED = "config.health_changed"
    TEMPLATE_ADDED = "template.added"
    TEMPLATE_UPDATED = "template.updated"
    TEMPLATE_SYNCED = "template.synced"
    BATCH_UPDATE = "config.batch_update"


class ConfigEvent:
    """配置事件数据结构。"""
    
    def __init__(
        self,
        event_type: ConfigEventType,
        model_id: str,
        user_id: str,
        data: Dict[str, Any],
        timestamp: Optional[datetime] = None
    ):
        self.event_type = event_type
        self.model_id = model_id
        self.user_id = user_id
        self.data = data
        self.timestamp = timestamp or datetime.utcnow()
        self.event_id = f"{event_type.value}:{model_id}:{self.timestamp.timestamp()}"
    
    def to_dict(self) -> Dict[str, Any]:
        """转换为字典。"""
        return {
            "event_id": self.event_id,
            "event_type": self.event_type.value,
            "model_id": self.model_id,
            "user_id": self.user_id,
            "data": self.data,
            "timestamp": self.timestamp.isoformat()
        }
    
    def to_json(self) -> str:
        """转换为JSON字符串。"""
        return json.dumps(self.to_dict())
    
    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "ConfigEvent":
        """从字典创建事件。"""
        return cls(
            event_type=ConfigEventType(data["event_type"]),
            model_id=data["model_id"],
            user_id=data["user_id"],
            data=data["data"],
            timestamp=datetime.fromisoformat(data["timestamp"])
        )


class ConfigEventManager:
    """配置事件管理器。"""
    
    def __init__(
        self,
        redis_client: redis.Redis,
        db: AsyncIOMotorDatabase,
        channel_prefix: str = "config_events"
    ):
        self.redis_client = redis_client
        self.db = db
        self.channel_prefix = channel_prefix
        self.websocket_connections: Dict[str, List[WebSocket]] = {}
        self.event_handlers: Dict[ConfigEventType, List[Callable]] = {}
        self.pubsub = None
        self._running = False
        self._task = None
    
    async def start(self):
        """启动事件管理器。"""
        if self._running:
            return
        
        self._running = True
        self.pubsub = self.redis_client.pubsub()
        
        # 订阅所有配置事件频道
        await self.pubsub.psubscribe(f"{self.channel_prefix}:*")
        
        # 启动事件监听任务
        self._task = asyncio.create_task(self._listen_events())
        logger.info("配置事件管理器已启动")
    
    async def stop(self):
        """停止事件管理器。"""
        if not self._running:
            return
        
        self._running = False
        
        if self.pubsub:
            await self.pubsub.punsubscribe()
            await self.pubsub.close()
        
        if self._task:
            self._task.cancel()
            try:
                await self._task
            except asyncio.CancelledError:
                pass
        
        logger.info("配置事件管理器已停止")
    
    async def _listen_events(self):
        """监听Redis事件。"""
        try:
            async for message in self.pubsub.listen():
                if message["type"] == "pmessage":
                    await self._handle_redis_message(message)
        except asyncio.CancelledError:
            pass
        except Exception as e:
            logger.error(f"事件监听错误: {e}")
    
    async def _handle_redis_message(self, message: Dict[str, Any]):
        """处理Redis消息。"""
        try:
            # 解析事件数据
            event_data = json.loads(message["data"])
            event = ConfigEvent.from_dict(event_data)
            
            # 触发事件处理器
            await self._trigger_handlers(event)
            
            # 发送WebSocket通知
            await self._notify_websockets(event)
            
            # 存储事件到数据库
            await self._store_event(event)
            
        except Exception as e:
            logger.error(f"处理Redis消息失败: {e}")
    
    async def publish_event(self, event: ConfigEvent):
        """发布事件到Redis。"""
        try:
            # 发布到用户特定频道
            channel = f"{self.channel_prefix}:{event.user_id}"
            await self.redis_client.publish(channel, event.to_json())
            
            # 如果是批量更新，发布到全局频道
            if event.event_type == ConfigEventType.BATCH_UPDATE:
                global_channel = f"{self.channel_prefix}:global"
                await self.redis_client.publish(global_channel, event.to_json())
            
            logger.debug(f"事件已发布: {event.event_id}")
            
        except Exception as e:
            logger.error(f"发布事件失败: {e}")
    
    def register_handler(self, event_type: ConfigEventType, handler: Callable):
        """注册事件处理器。"""
        if event_type not in self.event_handlers:
            self.event_handlers[event_type] = []
        
        self.event_handlers[event_type].append(handler)
        logger.info(f"已注册事件处理器: {event_type.value}")
    
    async def _trigger_handlers(self, event: ConfigEvent):
        """触发事件处理器。"""
        handlers = self.event_handlers.get(event.event_type, [])
        
        for handler in handlers:
            try:
                if asyncio.iscoroutinefunction(handler):
                    await handler(event)
                else:
                    handler(event)
            except Exception as e:
                logger.error(f"事件处理器执行失败: {e}")
    
    async def add_websocket(self, user_id: str, websocket: WebSocket):
        """添加WebSocket连接。"""
        if user_id not in self.websocket_connections:
            self.websocket_connections[user_id] = []
        
        self.websocket_connections[user_id].append(websocket)
        
        # 订阅用户频道
        await self.pubsub.subscribe(f"{self.channel_prefix}:{user_id}")
        
        logger.info(f"WebSocket连接已添加: {user_id}")
    
    async def remove_websocket(self, user_id: str, websocket: WebSocket):
        """移除WebSocket连接。"""
        if user_id in self.websocket_connections:
            if websocket in self.websocket_connections[user_id]:
                self.websocket_connections[user_id].remove(websocket)
            
            # 如果没有更多连接，清理并取消订阅
            if not self.websocket_connections[user_id]:
                del self.websocket_connections[user_id]
                await self.pubsub.unsubscribe(f"{self.channel_prefix}:{user_id}")
        
        logger.info(f"WebSocket连接已移除: {user_id}")
    
    async def _notify_websockets(self, event: ConfigEvent):
        """通过WebSocket发送通知。"""
        user_websockets = self.websocket_connections.get(event.user_id, [])
        
        if not user_websockets:
            return
        
        message = {
            "type": "config_event",
            "event": event.to_dict()
        }
        
        # 发送给所有连接的WebSocket
        disconnected = []
        for ws in user_websockets:
            try:
                await ws.send_json(message)
            except Exception as e:
                logger.warning(f"WebSocket发送失败: {e}")
                disconnected.append(ws)
        
        # 清理断开的连接
        for ws in disconnected:
            await self.remove_websocket(event.user_id, ws)
    
    async def _store_event(self, event: ConfigEvent):
        """存储事件到数据库。"""
        try:
            await self.db.config_events.insert_one({
                "event_id": event.event_id,
                "event_type": event.event_type.value,
                "model_id": event.model_id,
                "user_id": event.user_id,
                "data": event.data,
                "timestamp": event.timestamp
            })
        except Exception as e:
            logger.error(f"存储事件失败: {e}")
    
    async def get_event_history(
        self,
        user_id: str,
        model_id: Optional[str] = None,
        event_type: Optional[ConfigEventType] = None,
        limit: int = 100
    ) -> List[ConfigEvent]:
        """获取事件历史。"""
        try:
            query = {"user_id": user_id}
            
            if model_id:
                query["model_id"] = model_id
            
            if event_type:
                query["event_type"] = event_type.value
            
            events = await self.db.config_events.find(query)\
                .sort("timestamp", -1)\
                .limit(limit)\
                .to_list(None)
            
            return [ConfigEvent.from_dict({
                "event_type": e["event_type"],
                "model_id": e["model_id"],
                "user_id": e["user_id"],
                "data": e["data"],
                "timestamp": e["timestamp"].isoformat()
            }) for e in events]
            
        except Exception as e:
            logger.error(f"获取事件历史失败: {e}")
            return []
    
    async def broadcast_config_change(
        self,
        model_id: str,
        change_type: str,
        changes: Dict[str, Any],
        affected_users: List[str]
    ):
        """广播配置变更给受影响的用户。"""
        for user_id in affected_users:
            event = ConfigEvent(
                event_type=ConfigEventType.UPDATED,
                model_id=model_id,
                user_id=user_id,
                data={
                    "change_type": change_type,
                    "changes": changes,
                    "notified_at": datetime.utcnow().isoformat()
                }
            )
            await self.publish_event(event)
    
    async def notify_health_change(
        self,
        model_id: str,
        user_id: str,
        old_status: str,
        new_status: str,
        details: Optional[Dict[str, Any]] = None
    ):
        """通知健康状态变更。"""
        event = ConfigEvent(
            event_type=ConfigEventType.HEALTH_CHANGED,
            model_id=model_id,
            user_id=user_id,
            data={
                "old_status": old_status,
                "new_status": new_status,
                "details": details or {},
                "changed_at": datetime.utcnow().isoformat()
            }
        )
        await self.publish_event(event)


# 全局事件管理器实例
_event_manager: Optional[ConfigEventManager] = None


def get_event_manager() -> ConfigEventManager:
    """获取事件管理器实例。"""
    global _event_manager
    if _event_manager is None:
        raise RuntimeError("事件管理器未初始化")
    return _event_manager


async def init_event_manager(redis_client: redis.Redis, db: AsyncIOMotorDatabase):
    """初始化事件管理器。"""
    global _event_manager
    _event_manager = ConfigEventManager(redis_client, db)
    await _event_manager.start()
    return _event_manager


async def cleanup_event_manager():
    """清理事件管理器。"""
    global _event_manager
    if _event_manager:
        await _event_manager.stop()
        _event_manager = None


# 便捷函数
async def emit_config_created(model_id: str, user_id: str, config_data: Dict[str, Any]):
    """发送配置创建事件。"""
    event = ConfigEvent(
        event_type=ConfigEventType.CREATED,
        model_id=model_id,
        user_id=user_id,
        data=config_data
    )
    await get_event_manager().publish_event(event)


async def emit_config_updated(model_id: str, user_id: str, changes: Dict[str, Any]):
    """发送配置更新事件。"""
    event = ConfigEvent(
        event_type=ConfigEventType.UPDATED,
        model_id=model_id,
        user_id=user_id,
        data={"changes": changes}
    )
    await get_event_manager().publish_event(event)


async def emit_config_deleted(model_id: str, user_id: str, config_name: str):
    """发送配置删除事件。"""
    event = ConfigEvent(
        event_type=ConfigEventType.DELETED,
        model_id=model_id,
        user_id=user_id,
        data={"config_name": config_name}
    )
    await get_event_manager().publish_event(event)